package de.mud.jta.plugin;

import de.mud.jta.FilterPlugin;
import de.mud.jta.Plugin;
import de.mud.jta.PluginBus;
import de.mud.jta.PluginConfig;
import de.mud.jta.Repository;
import de.mud.jta.VisualPlugin;
import de.mud.jta.event.ConfigurationListener;
import de.mud.jta.event.SocketListener;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import java.net.UnknownHostException;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;


public class MFGUtil extends Plugin implements FilterPlugin, VisualPlugin {
	
	/* ========== Constant ========= */
	private final static String KEY_SEPARATOR = ",";
	private final static String REC_SEPARATOR = ":";
	private final static String SNAP_HEADER = "@";
	private final static String EXEC_HEADER = "!";
	private final static int LATENCY = 1000;
	private final static int KEY_LATENCY = 300;
	private static final String NEWLINE = "\n";
	
	
	// MFG/PRO Chui has a standard 80 x 24 Characters Screen.
	private final static int WIDTH = 80;
	private final static int HEIGHT = 24;
	
	/* ========== Field ========== */
    // Visual Menu
    private JMenu menu;
    private Thread currentThread;

    // Start time of player
    private long starttimep;

    // Start time of recorder
    private long starttimer;

    // Status of Recorder
    private boolean enable = false;

    // Output folder ( Configuration )
    private String snapfolder;
    
    // Script Home (Configuration)
    private String defaultFolder;

    /* =========Player========= */
    /**
     * This is the inner class for excuting file script.
     *
     * @author Jian Wu
     *
     */
    private class Player implements Runnable {
        private String filename;

        /**
         * Construct Player with the file which will be excuted when it start.
         *
         * @param filename
         */
        public Player(String filename) {
            super();
            this.filename = filename;
        } // end Player()

        public void run() {
            try {
                Thread.sleep(LATENCY);

                BufferedReader in = new BufferedReader(new FileReader(filename));

                String str = "";

                while ((str = in.readLine()) != null) {
                    starttimep = System.currentTimeMillis();


                    System.out.println(str);
                    write(getKey(str));
                } // end while

                in.close();
            } // end try
            catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } // end catch
        } // end run()

        /**
         * Retrive the key.
         *
         * @param str
         * @return
         */
        private byte[] getKey(String str) {
            String time = str.split(":")[0];
            String key = str.split(":")[1];

            long timeLong = Long.parseLong(time);

            // Wait until current time is larger than the timestamp(key lantency is still counted)
            while (timeLong > (System.currentTimeMillis() - starttimep - KEY_LATENCY)) {
                Thread.yield();
            } // end while
            
            byte[] returnKeys = null;            
            try {
                // Snapshoot
            	if (key.startsWith(SNAP_HEADER)) {
				
				    String snapfilename = key.substring(1);
				    snapfilename = snapfolder + "\\" + snapfilename;

				    FileWriter snapfw = new FileWriter(snapfilename);
				    printScreen(snapfw, Repository.buffer.charArray);
				    snapfw.close();
				    returnKeys = new byte[0];
				} // end if
				
				// Execute other script.
				if (key.startsWith(EXEC_HEADER)) {
					Player newplayer = new Player(loadDir().getPath() + "\\" + key.substring(1));
					newplayer.run();
				    returnKeys = new byte[0];
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			// Write to the buffer.
			if (returnKeys == null) {
	            String[] keys = key.split(KEY_SEPARATOR);
	            returnKeys = new byte[keys.length];
	
	            for (int i = 0; i < keys.length; i++) {
	                returnKeys[i] = Byte.parseByte(keys[i]);
	            } // end for
			}

            return returnKeys;
        } // end getKey()
    } // end Player

    /* =========FileterPlugin========= */

    /**
     * Constructor of MFGUtil.
     *
     * @param bus
     * @param id
     */
    public MFGUtil(PluginBus bus, final String id) {
        super(bus, id);
     
        // Get configuation from properties
        bus.registerPluginListener(new ConfigurationListener() {
                public void setConfiguration(PluginConfig config) {
                    snapfolder = config.getProperty("MFGUtil", id,
                            "snapshoot_folder");
                    defaultFolder = config.getProperty("MFGUtil", id,
                    		"default_folder");

                } // end setConfiguration()
            } // end new
        );

        // Status Listener
        bus.registerPluginListener(new SocketListener() {
                public void connect(String host, int port)
                    throws UnknownHostException, IOException {
                } // end connect()

                public void disconnect() throws IOException {
                    if (fw != null) {
                        fw.close();
                        System.out.println("=== Rec End ===");
                        fw = null;
                    } // end if
                } // end disconnect()
            } // end new
        );
        
        // Menu Initialization
        menu = new JMenu("MFG Util");

        final JCheckBoxMenuItem rec = new JCheckBoxMenuItem("Rec", false);
        rec.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (!enable) {
                        JFileChooser fileChooser = new JFileChooser();

                        fileChooser.setCurrentDirectory(loadDir());
                        if (fileChooser.showSaveDialog(Repository.frameMain) == JFileChooser.APPROVE_OPTION) {
                            starttimer = System.currentTimeMillis();

                            try {
                                fw = new FileWriter(fileChooser.getSelectedFile()
                                                               .getPath());
                            } // end try
                            catch (IOException exception) {
                                // TODO Auto-generated catch block
                                exception.printStackTrace();
                            } // end catch

                            enable = true;
                        } // end if
                        else {
                            rec.setSelected(false);
                        } // end else
                    } // end if
                    else {
                        try {
                            if (fw != null) {
                                fw.close();

                                System.out.println("=== Rec End ===");
                                fw = null;
                            } // end if
                        } // end try
                        catch (IOException exception) {
                            // TODO Auto-generated catch block
                            exception.printStackTrace();
                        } // end catch

                        enable = false;
                    } // end else
                } // end actionPerformed()
            } // end new
        );

        JMenuItem playfile = new JMenuItem("Play File");
        playfile.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    JFileChooser fileChooser = new JFileChooser();
                    fileChooser.setCurrentDirectory(loadDir());
                    if (fileChooser.showOpenDialog(Repository.frameMain) == JFileChooser.APPROVE_OPTION) {
                        starttimep = System.currentTimeMillis();
                        
                        // Once the player was executed, the snapfolder will be decidied.
                        String folder = snapfolder + "\\" + System.currentTimeMillis();
                        // Do util the folder is created successfully.
                        while (!(new File(folder)).mkdirs()){
                        	folder = snapfolder + "\\" + System.currentTimeMillis();
                        }
                        snapfolder = folder;

                        currentThread = new Thread(new Player(
                                    fileChooser.getSelectedFile().getPath()));

                        currentThread.start();
                    } // end if

                    fileChooser = null;
                } // end actionPerformed()
            } // end new
        );

        menu.add(rec);

        menu.add(playfile);

        // TODO Auto-generated constructor stub
    } // end MFGUtil()

    protected File loadDir() {
        final File dir = new File(defaultFolder);
        
        boolean isDir = dir.isDirectory();
        if (!isDir) {
        	System.out.println("MFGUtil.default_folder setting is incorrect!");
        	System.exit(-1);
        	// dir is a file
        }
        return dir;
	}

	private FileWriter fw;

    /* =========FileterPlugin========= */
    protected FilterPlugin source;

    public void setFilterSource(FilterPlugin source) {
        System.out.println(source);
        this.source = source;
    } // end setFilterSource()

    public FilterPlugin getFilterSource() {
        return source;
    } // end getFilterSource()

    public int read(byte[] b) throws IOException {
        // printBytes(b);
        return source.read(b);
    } // end read()

    public void write(byte[] b) throws IOException {
        source.write(b);
        
        // If recorder is running, write the bytes
        if (enable) {
            printBytes(b);
        } // end if
    } // end write()

    /**
     * Write the file to 
     * @param b
     */
    private void printBytes(byte[] b) {
        StringBuilder sb = new StringBuilder();
        sb.append(System.currentTimeMillis() - starttimer);
        starttimer = System.currentTimeMillis();
        sb.append(REC_SEPARATOR);

        for (int i = 0; i < b.length; i++) {
            sb.append(b[i]);
            sb.append(KEY_SEPARATOR);
        } // end for
        
        writeString(fw, sb.toString());
    } // end printBytes()

    /* =========VisualPlugin========= */
    public JComponent getPluginVisual() {
        // TODO Auto-generated method stub
        return null;
    } // end getPluginVisual()

    public JMenu getPluginMenu() {
        return menu;
    } // end getPluginMenu()

    private static void writeString(FileWriter fw, String str) {
        try {
            fw.write(str + NEWLINE);
        } // end try
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } // end catch

        //System.out.println(str);
    } // end writeString()

    private static void printScreen(FileWriter fw, char[][] charArray) {
        int start = charArray.length - HEIGHT;

        for (int i = start; i < charArray.length; i++) {
            String s = "";

            for (int j = 0; j < WIDTH; j++) {
                s = s + charArray[i][j];
            } // end for

            writeString(fw, s);

            // System.out.println(s);
        } // end for
    } // end printScreen()
} // end MFGUtil
